﻿Option Explicit On
Option Strict On

Public Class Logger32Interface
    Inherits Windows.Forms.Form

#Region " Private Members "

    Private WithEvents m_RetryTimer As Windows.Forms.Timer
    Private WithEvents m_Logger32ShutdownTimer As Windows.Forms.Timer 'Monitors the Logger32 window to determine if it's been closed 'TODO: Could replace this with SetWinEventHook API call to receive a notification rather than polling

    Private m_Connected As Boolean = False
    Private m_Logger32_hWnd As IntPtr 'The hWnd of Logger32
    Private m_Logger32_MSGID As Integer 'The ID of the Windows Message to use when communicating with Logger32
    Private m_Logger32_MessageNameCounter As Integer = 5 'The Suffix number for the message name, will count up to 5 and retry 1 again if the connection is refused

    Private m_CurrentRadioID As Integer

#End Region

#Region " Constants & Enums "

    Private RETRY_TIMER_INTERVAL As Integer = 5000

    Private LOGGER32_WINDOW_NAME As String = "Logger32" 'The name of the Logger32 Window to look for when communicating with Logger32
    Private LOGGER32_MESSAGE_PREFIX As String = "Logger32" 'The prefix for the name of the windows message to use when communicating with Logger32, must be in the format "Logger32 X" where X is a number

    Private Enum LOGGER32_MessageID As Integer
        ConnectionReponse = 0 'Logger32 is responding to a connection attempt
        ADIHwnd = 3 'Logger32 is sending the hWnd of a textbox for sending ADI formatted text strings to
        CurrentRadio = 99 'Logger32 is telling us which radio is in use
        CurrentFrequency = 100 'Logger32 is telling us what the frequency of the current radio is
    End Enum

    Private Enum LOGGER32_CommandID As Integer
        SetupInboundTextBox = 2 'Allows us to tell Logger32 the hWnd of a Textbox it can use when sending us unsolicited data
        SetDXSpotCommunicationPreference = 2 'Allows us to tell Logger32 if we want DX Spot information or not
        InterfaceClosing = 4 'Tells Logger32 we are closing the interface
    End Enum

#End Region

#Region " Events "

    Public Event Logger32Connected As EventHandler(Of EventArgs)
    Public Event Logger32Disconnected As EventHandler(Of EventArgs)
    Public Event Logger32RadioNumberChanged As EventHandler(Of Logger32RadioNumberChangedEventArgs)
    Public Event Logger32FrequencyChanged As EventHandler(Of Logger32FrequencyChangedEventArgs)

#End Region

#Region " Public Methods "

    ''' <summary>
    ''' Starts the Interface and attempt a connection to logger32
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub Connect()

        'Setup the Retry Timer
        If m_RetryTimer IsNot Nothing Then
            m_RetryTimer.Stop()
            m_RetryTimer.Dispose()
        End If
        m_RetryTimer = New Windows.Forms.Timer
        m_RetryTimer.Interval = RETRY_TIMER_INTERVAL
        m_RetryTimer.Start()

        'Make the initial connection attempt
        ConnectToLogger32()
    End Sub

    ''' <summary>
    ''' Disconnects from Logger32
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub Disconnect()
        DisconnectFromLogger32()
    End Sub

#End Region

#Region " Properties "

    ''' <summary>
    ''' The Connected state of the interface
    ''' </summary>
    Public ReadOnly Property Connected As Boolean
        Get
            Return m_Connected
        End Get
    End Property

#End Region

#Region " Private Methods "

    ''' <summary>
    ''' Handles the WndProc and Intercepts the Logger32 Messages
    ''' </summary>
    ''' <param name="m"></param>
    ''' <remarks></remarks>
    Protected Overrides Sub WndProc(ByRef m As Message)

        Select Case m.Msg
            Case m_Logger32_MSGID
                Select Case m.WParam.ToInt32
                    Case LOGGER32_MessageID.ConnectionReponse  'Response to L32 connection request
                        HandleLogger32ConnectionRequest(m)
                    Case LOGGER32_MessageID.CurrentRadio  'Logger32 radio in use
                        m_CurrentRadioID = m.LParam.ToInt32

                        If Logger32RadioNumberChangedEvent IsNot Nothing Then
                            RaiseEvent Logger32RadioNumberChanged(Me, New Logger32RadioNumberChangedEventArgs(m_CurrentRadioID))
                        End If
                    Case LOGGER32_MessageID.CurrentFrequency  ' Frequency
                        'frmMain.Caption = RadioInUse & " - Freq is: " & Format$(lParam / 1000, "#.000") & " KHz"

                        If Logger32FrequencyChangedEvent IsNot Nothing Then
                            RaiseEvent Logger32FrequencyChanged(Me, New Logger32FrequencyChangedEventArgs(m.LParam.ToInt32, m_CurrentRadioID))
                        End If
                End Select
            Case Else
                MyBase.WndProc(m)
        End Select

    End Sub

    Protected Overrides Sub Dispose(disposing As Boolean)
        'Ensure we have told Logger32 we are disconnecting
        DisconnectFromLogger32()

        MyBase.Dispose(disposing)
    End Sub

    ''' <summary>
    ''' Disconnects from Logger32
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub DisconnectFromLogger32()
        DestroyTimers()

        If m_Connected Then
            Call UnsafeNativeMethods.PostMessage(m_Logger32_hWnd, CUInt(m_Logger32_MSGID), New IntPtr(LOGGER32_CommandID.InterfaceClosing), Me.Handle)
            m_Connected = False

            RaiseEvent Logger32Disconnected(Me, New EventArgs)
        End If
    End Sub

    Private Sub DestroyTimers()
        If m_Logger32ShutdownTimer IsNot Nothing Then
            m_Logger32ShutdownTimer.Stop()
            m_Logger32ShutdownTimer.Dispose()
            m_Logger32ShutdownTimer = Nothing
        End If

        If m_RetryTimer IsNot Nothing Then
            m_RetryTimer.Stop()
            m_RetryTimer.Dispose()
            m_RetryTimer = Nothing
        End If
    End Sub

    ''' <summary>
    ''' Sends the Connection Message to Logger32
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub ConnectToLogger32()
        m_Logger32_hWnd = UnsafeNativeMethods.FindWindow(Nothing, LOGGER32_WINDOW_NAME)

        If Not m_Logger32_hWnd = IntPtr.Zero Then
            'increment the Message name Counter   
            If m_Logger32_MessageNameCounter = 5 Then
                m_Logger32_MessageNameCounter = 1
            Else
                m_Logger32_MessageNameCounter += 1
            End If

            m_Logger32_MSGID = UnsafeNativeMethods.RegisterWindowMessage(LOGGER32_MESSAGE_PREFIX & " " & m_Logger32_MessageNameCounter.ToString)
            Call UnsafeNativeMethods.PostMessage(m_Logger32_hWnd, CUInt(m_Logger32_MSGID), New IntPtr(1), Me.Handle)
        End If
    End Sub

    ''' <summary>
    ''' Retries connections to Logger32
    ''' </summary>
    Private Sub m_RetryTimer_Tick(sender As Object, e As EventArgs) Handles m_RetryTimer.Tick
        ConnectToLogger32()
    End Sub

    Private Sub m_Logger32ShutdownTimer_Tick(sender As Object, e As EventArgs) Handles m_Logger32ShutdownTimer.Tick
        Dim Logger32hWnd As IntPtr = UnsafeNativeMethods.FindWindow(Nothing, LOGGER32_WINDOW_NAME)
        If Logger32hWnd = IntPtr.Zero Then
            'Logger32 Was closed
            m_Logger32ShutdownTimer.Stop()

            DestroyTimers()
            RaiseEvent Logger32Disconnected(Me, New EventArgs)
        End If
    End Sub

#Region " Message Handlers "

    ''' <summary>
    ''' Handles the Logger32 Connection Response
    ''' </summary>
    ''' <param name="m"></param>
    ''' <remarks></remarks>
    Private Sub HandleLogger32ConnectionRequest(ByVal m As Message)

        'We have received a connection response, turn off the timer while we process it
        m_RetryTimer.Enabled = False

        'Has our connection been accepted?
        If m.LParam.ToInt32 = 0 Then ' Rejected
            'Connection rejected so restart the retry timer       
            m_Connected = False
            m_RetryTimer.Enabled = True
        Else ' Accepted
            'Our connection was accepted, now we need to setup some connection parameters
            m_Connected = True

            'We don't want unsolicited data, so tell Logger32 not to send it
            UnsafeNativeMethods.PostMessage(m_Logger32_hWnd, CUInt(m_Logger32_MSGID), New IntPtr(LOGGER32_CommandID.SetupInboundTextBox), IntPtr.Zero)

            'We don't want any DX Spot information, so explicitly tell Logger32 not to send it
            'This is probably unneccesary, but we'll send the command anyway to be on the safe side
            UnsafeNativeMethods.PostMessage(m_Logger32_hWnd, CUInt(m_Logger32_MSGID), New IntPtr(LOGGER32_CommandID.SetDXSpotCommunicationPreference), IntPtr.Zero)

            m_Logger32ShutdownTimer = New Windows.Forms.Timer
            m_Logger32ShutdownTimer.Interval = 5000
            m_Logger32ShutdownTimer.Start()

            RaiseEvent Logger32Connected(Me, New EventArgs)
        End If

    End Sub

#End Region

#End Region

End Class
